/*******************************************************************************
 * This file is part of Crunch Network.
 *
 * Crunch Network is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * Crunch Network is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with
 * Crunch Network.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/

package com.crunch.network.util;

/**
 * Used to calculate an exponentially weighted moving average.
 */
public class ExponentiallyWeightedMovingAverage {
	/**
	 * Constructs the moving average.
	 *
	 * @param   weight the weight applied to the newest data points as they are added.
	 */
	public ExponentiallyWeightedMovingAverage(float weight) {
		this.weight = weight;
		clear();
	}

	/**
	 * Sets the weight applied to the newest data points as they are added.
	 *
	 * @param   weight the weight applied to new data points.
	 */
	public void setWeight(float weight) {
		this.weight = weight;
	}

	/**
	 * Removes all data points and resets the average.
	 */
	public void clear() {
		average = 0.0f;
		variance = 0.0f;
		dataPoints = 0;
	}

	/**
	 * Adds a data point, recomputing average and variance.
	 *
	 * @param   value the data point to add.
	 */
	public void addDataPoint(float value) {
		if (dataPoints == 0) {
			average = value;
			variance = 0.0f;
		} else {
			// compute exponential moving average and variance - variance first
			// see http://nfs-uxsup.csx.cam.ac.uk/~fanf2/hermes/doc/antiforgery/stats.pdf
			float diff = value - average;
			float inc = weight * diff;
			average += inc;
			variance = (1.0f - weight) * (variance + diff*inc);
		}
	}

	/**
	 * Returns the weight applied to the newest data points as they are added.
	 *
	 * @return the weight applied to new data points.
	 */
	public float getWeight() {
		return weight;
	}

	/**
	 * Returns the current average.
	 *
	 * @return  the current average.
	 */
	public float getAverage() {
		return average;
	}

	/**
	 * Returns the current variance.
	 *
	 * @return  the current variance.
	 */
	public float getVariance() {
		return variance;
	}

	/**
	 * Returns the current number of data points.
	 *
	 * @return  the current number of data points.
	 */
	public int getDataPointCount() {
		return dataPoints;
	}

	private float weight;
	private float average;
	private float variance;
	private int dataPoints;
}
